package com.food.util;

import java.util.*;

/**
 * @author beixiang
 * @version 1.0
 * @description UserCF(基于用户的协同过滤算法)，用户的个性化美食推荐
 * @date 2023/7/20 16:02
 */
public class UserCFUtil {
    // 用户-美食-评分数据
    private Map<Integer, Map<Integer, Double>> userFoodMap;
    // 相似度矩阵
    private Map<Integer, Map<Integer, Double>> similarityMatrix;

    public UserCFUtil() {
        userFoodMap = new HashMap<>();
        similarityMatrix = new HashMap<>();
    }

    // 添加用户-美食-评分数据
    public void addUserFood(Integer userId, Integer foodId, double rating) {
        if (!userFoodMap.containsKey(userId)) {
            userFoodMap.put(userId, new HashMap<>());
        }
        userFoodMap.get(userId).put(foodId, rating);
    }

    // 计算相似度矩阵
    public void computeSimilarityMatrix() {
        for (Integer user1 : userFoodMap.keySet()) {
            for (Integer user2 : userFoodMap.keySet()) {
                if (!similarityMatrix.containsKey(user1)) {
                    similarityMatrix.put(user1, new HashMap<>());
                }
                // 计算两个用户之间的相似度
                double similarity = computeSimilarity(user1, user2);
                similarityMatrix.get(user1).put(user2, similarity);
            }
        }
    }

    // 计算两个用户之间的相似度
    private double computeSimilarity(Integer user1, Integer user2) {
        Map<Integer, Double> ratings1 = userFoodMap.get(user1);
        Map<Integer, Double> ratings2 = userFoodMap.get(user2);

        // 获取两个用户共同收藏的美食
        Set<Integer> commonFoods = new HashSet<>(ratings1.keySet());
        commonFoods.retainAll(ratings2.keySet());

        if (commonFoods.isEmpty()){
            return 0;
        }

        double numerator = 0.0;
        double denominator1 = 0.0;
        double denominator2 = 0.0;

        // 计算相似度的分子和分母
        for (Integer foodId : commonFoods) {
            double rating1 = ratings1.get(foodId);
            double rating2 = ratings2.get(foodId);
            numerator += rating1 * rating2;
            denominator1 += rating1 * rating1;
            denominator2 += rating2 * rating2;
        }
        // 返回相似度
        double similarity = numerator / (Math.sqrt(denominator1) * Math.sqrt(denominator2));
        return similarity;
    }

    // 根据用户id基于相似度矩阵获取美食推荐列表（返回前N个）
    public List<Integer> getFoodRecommendations(Integer userId, int topN) {
        if (!userFoodMap.containsKey(userId)) {
            return Collections.emptyList();
        }
        Map<Integer, Double> userRatings = userFoodMap.get(userId);
        Map<Integer, Double> recommendations = new HashMap<>();
        for (Integer otherUser : userFoodMap.keySet()) {
            if (!otherUser.equals(userId)) {
                Map<Integer, Double> otherUserRatings = userFoodMap.get(otherUser);
                for (Integer foodId : otherUserRatings.keySet()) {
                    if (!userRatings.containsKey(foodId)) {
                        if (!recommendations.containsKey(foodId)) {
                            recommendations.put(foodId, 0.0);
                        }
                        //获得用户相似度
                        double similarity = similarityMatrix.get(userId).get(otherUser);
                        double rating = otherUserRatings.get(foodId);
                        // 累积美食的推荐分
                        recommendations.put(foodId, recommendations.get(foodId) + similarity * rating);
                    }
                }
            }
        }
        // 将推荐列表按照推荐分降序排序
        List<Map.Entry<Integer, Double>> sortedRecommendations = new ArrayList<>(recommendations.entrySet());
        sortedRecommendations.sort(Collections.reverseOrder(Map.Entry.comparingByValue()));
        List<Integer> topRecommendations = new ArrayList<>();
        for (int i = 0; i < topN && i < sortedRecommendations.size(); i++) {
            Integer foodId = sortedRecommendations.get(i).getKey();
            topRecommendations.add(foodId);
        }
        return topRecommendations;
    }
}